package com.lili.collection.restful.base;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.Method;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.lili.collection.core.enums.FieldType;
import com.lili.collection.core.utils.JSONConfigUtil;
import com.lili.collection.restful.config.InterfaceConfig;
import com.lili.collection.restful.constant.DsConstPool;
import com.lili.collection.restful.constant.InterfaceConstPool;
import com.lili.collection.restful.exception.ServiceException;
import com.alibaba.fastjson2.JSON;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * restful执行请求
 *
 * @author: lili
 * @since: 2023/3/9 16:23
 */
@Slf4j
public class RestfulRequest implements IInterfaceRequest {
    /**
     * 执行接口请求（兼容数据源配置默认参数执行以及动态传参执行）
     *
     * @param config: 接口配置
     * @param params: 参数信息
     * @author: lili
     * @date: 2023/3/11 16:47
     * @return: java.lang.Object
     */
    @Override
    public Object getData(InterfaceConfig config, Map<String, Object> params) {
        //动态传参的条件
        Map<String, Object> where = JSONUtil.parseObj(params.get(DsConstPool.WHERE), JSONConfigUtil.getConfig());

        //处理url：包括path和query的处理
        String url = processingUrl(config, where);
        //存放请求体
        Map<String, Object> body = processingBody(config, where);
        //请求类型
        String requestType = config.getRequestMethodType();

        JSONObject headerObj = JSONUtil.parseObj(config.getReqHeader());
        Map<String, String> header = new HashMap<>(headerObj.size());
        headerObj.keySet().forEach(key -> header.put(key, headerObj.getStr(key)));
        //构建http请求
        HttpRequest request = createHttpMethod(requestType, url)
                .method(Method.valueOf(requestType))
                .headerMap(header, true)
                .body(JSONUtil.toJsonStr(body))
                .timeout(config.getTimeOut() * 1000);
        //执行请求
        try (HttpResponse response = request.execute()) {
            //返回执行结果的字符串
            return response.body();
        } catch (Exception e) {
            log.error("请求地址：{},请求配置：{}，请求参数：{}，异常信息：{}", url, JSONUtil.toJsonStr(config), JSONUtil.toJsonStr(params), e.getMessage());
            throw new ServiceException("接口执行异常：" + e.getMessage());
        }
    }

    @Override
    public Object pushOutputData(InterfaceConfig config, String pushData) {
        //处理url：包括path和query的处理
        String reqTransMode = config.getReqTransMode();
        //存放请求体
        Map<String,Object> params=  null;
        if(InterfaceConstPool.PATH.equalsIgnoreCase(reqTransMode)
                || InterfaceConstPool.QUERY.equalsIgnoreCase(reqTransMode)){
            params = JSON.parseObject(pushData);
        }
        String url = processingPushDataUrl(config, params);
        //请求类型
        String requestType = config.getRequestMethodType();
        JSONObject headerObj = JSONUtil.parseObj(config.getReqHeader());
        Map<String, String> header = new HashMap<>(headerObj.size());
        headerObj.keySet().forEach(key -> header.put(key, headerObj.getStr(key)));
        //构建http请求
        HttpRequest request = createHttpMethod(requestType, url)
                .method(Method.valueOf(requestType))
                .headerMap(header, true)
                .timeout(config.getTimeOut() * 1000);
        if(InterfaceConstPool.BODY.equalsIgnoreCase(reqTransMode)){
            request.body(pushData);
        }
        //执行请求
        try (HttpResponse response = request.execute()) {
            //返回执行结果的字符串
            return response.body();
        } catch (Exception e) {
            log.error("请求地址：{},请求配置：{}，请求参数：{}，异常信息：{}", url, JSONUtil.toJsonStr(config), JSONUtil.toJsonStr(params), e.getMessage());
            throw new ServiceException("接口执行异常：" + e.getMessage());
        }
    }


    /**
     * 根据请求类型和url创建请求
     *
     * @param requestType: 请求类型
     * @param url:         完整的url串
     * @author: lili
     * @date: 2023/3/11 16:54
     * @return: 请求
     */
    private HttpRequest createHttpMethod(String requestType, String url) {
        Method method = Method.valueOf(requestType);
        if (Method.GET.equals(method)) {
            return HttpRequest.get(url);
        } else if (Method.POST.equals(method)) {
            return HttpRequest.post(url);
        } else if (Method.PUT.equals(method)) {
            return HttpRequest.put(url);
        } else if (Method.DELETE.equals(method)) {
            return HttpRequest.delete(url);
        }
        throw new ServiceException("不支持该请求方法：" + requestType);
    }

    /**
     * 获取最终的body请求报文
     *
     * @param config: 接口配置
     * @param where:  动态条件
     * @author: lili
     * @date: 2023/3/11 16:55
     * @return: body参数报文
     */
    private Map<String, Object> processingBody(@NonNull InterfaceConfig config, Map<String, Object> where) {
        //请求传参方式
        String reqTransMode = config.getReqTransMode();
        if (InterfaceConstPool.BODY.equalsIgnoreCase(reqTransMode)) {
            List<ParameterDef> body = config.getReqParams();
            //说明是非运行时参数
            boolean noWhere = ObjectUtil.isEmpty(where) || where.size() == 0;
            if (!ObjectUtil.isEmpty(body)) {
                //转换成默认参数
                Map<String, Object> stringObjectMap = handlerArrayToObj(body);
                //如果有运行时参数
                if (!noWhere) {
                    //运行时最终请求参数
                    Map<String, Object> result = new HashMap<>();
                    //body类型参数只校验第一层的必填和是否默认，因为是根据定义，故运行时多余的参数会被忽略
                    for (ParameterDef param : body) {
                        String key = param.getKey().trim();
                        //运行参数值
                        Object whereValue = where.get(param.getKey());
                        //如果参数值为空且必填
                        if (ObjectUtil.isEmpty(whereValue)) {
                            if (Boolean.TRUE.equals(param.getMust())) {
                                throw new ServiceException("Body参数[" + key + "]为必填参数，不能为空！");
                            }
                            break;
                        }
                        result.put(key, whereValue);
                    }
                    return result;
                }
                return stringObjectMap;
            }
        }
        return Collections.emptyMap();
    }

    /**
     * 将接口的path、body、header、query参数定义转换为实际的对象
     *
     * @param arrayObj: 参数数组定义
     * @author: lili
     * @date: 2023/3/11 16:57
     * @return: 实际的对象
     */
    private Map<String, Object> handlerArrayToObj(Object arrayObj) {
        if (ObjectUtil.isEmpty(arrayObj)) {
            return null;
        }
        if (!JSONUtil.isJsonArray(JSONUtil.toJsonStr(arrayObj))) {
            throw new ServiceException("参数格式错误，要求的格式为数组！");
        }
        JSONArray array = JSONUtil.parseArray(arrayObj, JSONConfigUtil.getConfig());
        Map<String, Object> result = new HashMap<>(array.size());
        for (Object object : array) {
            Map<String, Object> obj = JSONUtil.parseObj(object,JSONConfigUtil.getConfig());
            String fieldType = (String) obj.get(InterfaceConstPool.FIELD_TYPE);
            String key = (String) obj.get(InterfaceConstPool.KEY);
            if (FieldType.OBJECT.getCode().equals(fieldType)) {
                result.put(key, handlerArrayToObj(obj.get(InterfaceConstPool.CHILDREN)));
            } else if (FieldType.ARRAY.getCode().equals(fieldType)) {
                //modify by lili 20230608 支持基本类型的数组
                String childrenType = (String) obj.get(InterfaceConstPool.CHILDREN_FIELD_TYPE);
                //为支持基本类型之前，不存在该值（默认为对象）
                if (CharSequenceUtil.isBlank(childrenType) || FieldType.OBJECT.getCode().equals(childrenType)) {
                    JSONArray objects = new JSONArray();
                    objects.add(handlerArrayToObj(obj.get(InterfaceConstPool.CHILDREN)));
                    result.put(key, objects);
                } else {
                    //当为基本类型时，其值直接取value,示例：1,2,3
                    Object o = obj.get(InterfaceConstPool.VALUE);
                    if (ObjectUtil.isNotEmpty(o)){
                        String str = (String)o;
                        result.put(key, str.split(","));
                    }
                }
            } else {
                //不是以上类型就取默认值
                result.put(key, obj.get(InterfaceConstPool.VALUE));
            }
        }
        return result;
    }

    /**
     * 获取最终的url，只会处理GET和DELETE类型请求的Path和Query传参
     * 处理推送形式的url
     * @param config 接口配置
     * @param params 参数
     */
    private String processingPushDataUrl(@NonNull InterfaceConfig config, Map<String, Object> params) {
        final String[] url = {config.getUrl()};
        //请求传参方式
        String reqTransMode = config.getReqTransMode();
        //非运行时参数：where为空或者where的size==0
        boolean hasParams = MapUtils.isNotEmpty(params);
        List<ParameterDef> paramsDefs = config.getReqParams();
        boolean hasParamsDef = CollectionUtils.isNotEmpty(paramsDefs);
        //path传参
        if (InterfaceConstPool.PATH.equalsIgnoreCase(reqTransMode) && hasParams && hasParamsDef) {
            paramsDefs.stream()
                    //先过滤url中需要替换的key
                    .filter(paramDef -> config.getUrl().contains(paramDef.getKey())).collect(Collectors.toList())
                    //替换url中path参数  /xxx/xx/:id/:name -> /xxx/xx/ID实际值/name实际值
                    .forEach(paramDef -> {
                        String key = paramDef.getKey().trim();
                        //默认值
                        Object value =  params.get(key);
                        if(StringUtils.isNotBlank(key) && ObjectUtil.isNotEmpty(value)){
                            //参数替换
                            url[0] = url[0].replace(":" + key, String.valueOf(value));
                        }else {
                            throw new ServiceException("Path参数[" + key + "]为必填参数，不能为空！");
                        }
                    });
            return url[0];
        } else if (InterfaceConstPool.QUERY.equalsIgnoreCase(reqTransMode) && hasParams && hasParamsDef) {
            //query参数 url --> url?id=xxx
            url[0] += "?";
            for (ParameterDef param : paramsDefs) {
                String key = param.getKey().trim();
                Object value =  params.get(key);
                if (StrUtil.isBlankIfStr(value) && Boolean.TRUE.equals(param.getMust())) {
                    throw new ServiceException("Query参数[" + key + "]为不能为空！");
                }else if (ObjectUtil.isEmpty(value)){
                    continue;
                }
                url[0] += key + "=" + value + "&";
            }
            //这里url最后字符不是?就是&
            return url[0].substring(0, url[0].length() - 1);
        }
        return url[0];
    }


    /**
     * 获取最终的url，只会处理GET和DELETE类型请求的Path和Query传参
     *
     * @param config: 接口配置
     * @param where:  动态查询条件
     * @author: lili
     * @date: 2023/3/9 16:37
     * @return: 最终url
     */
    private String processingUrl(@NonNull InterfaceConfig config, Map<String, Object> where) {
        final String[] url = {config.getUrl()};
        //请求传参方式
        String reqTransMode = config.getReqTransMode();
        //非运行时参数：where为空或者where的size==0
        boolean noWhere = ObjectUtil.isEmpty(where) || where.size() == 0;
        //path传参
        if (InterfaceConstPool.PATH.equalsIgnoreCase(reqTransMode)) {
            List<ParameterDef> pathParams = config.getReqParams();
            if (ObjectUtil.isNotNull(pathParams)) {
                pathParams.stream()
                        //先过滤url中需要替换的key
                        .filter(param -> config.getUrl().contains(param.getKey())).collect(Collectors.toList())
                        //替换url中path参数  /xxx/xx/:id/:name -> /xxx/xx/ID实际值/name实际值
                        .forEach(param -> {
                            String key = param.getKey().trim();
                            //默认值
                            Object value = param.getValue();
                            //如果有运行时参数
                            if (!noWhere) {
                                //运行参数值
                                Object whereValue = where.get(param.getKey());
                                //非空则默认使用运行时参数值
                                if (!StrUtil.isBlankIfStr(whereValue)) {
                                    value = whereValue;
                                } else {
                                    throw new ServiceException("接口数据源定义运行时Path参数[" + key + "]为必填参数，不能为空！");
                                }
                            }
                            //对于url中:xxx的参数必须替换和校验
                            if (StrUtil.isBlankIfStr(value)) {
                                throw new ServiceException("Path参数[" + key + "]为必填参数，不能为空！");
                            }
                            //参数替换
                            url[0] = url[0].replace(":" + key, String.valueOf(value));
                        });
            }
            return url[0];
        } else if (InterfaceConstPool.QUERY.equalsIgnoreCase(reqTransMode)) {
            //query参数 url --> url?id=xxx
            List<ParameterDef> queryParams = config.getReqParams();
            boolean aNull = ObjectUtil.isNull(queryParams);
            if (aNull || queryParams.isEmpty()) {
                return config.getUrl();
            }
            url[0] += "?";
            for (ParameterDef param : queryParams) {
                String key = param.getKey().trim();
                Object value = param.getValue();
                if (!noWhere) {
                    //运行参数值
                    Object whereValue = where.get(param.getKey());
                    //非空则默认使用运行时参数值
                    if (!StrUtil.isBlankIfStr(whereValue)) {
                        value = whereValue;
                    } else {
                        value = null;
                    }
                }
                if (StrUtil.isBlankIfStr(value) && Boolean.TRUE.equals(param.getMust())) {
                    throw new ServiceException("Query参数[" + key + "]为不能为空！");
                }else if (ObjectUtil.isEmpty(value)){
                    continue;
                }
                url[0] += key + "=" + value + "&";
            }
            //这里url最后字符不是?就是&
            return url[0].substring(0, url[0].length() - 1);
        }
        return url[0];
    }
}